import * as THREE from 'three';

// 相机控制器 - 支持拖拽旋转、平移，限制上下旋转角度
export class CameraController {
    constructor(camera, domElement) {
        this.camera = camera;
        this.domElement = domElement;
        
        // 目标点（相机看向的位置）
        this.target = new THREE.Vector3(0, 0, 0);
        
        // 球形坐标（用于旋转）
        this.spherical = new THREE.Spherical();
        this.sphericalDelta = new THREE.Spherical();
        
        // 平移
        this.panOffset = new THREE.Vector3();
        
        // 缩放
        this.scale = 1;
        this.zoomSpeed = 1.0;
        
        // 旋转速度
        this.rotateSpeed = 1.0;
        
        // 平移速度
        this.panSpeed = 1.0;
        
        // 垂直旋转限制（弧度）
        this.minPolarAngle = 0; // 最小仰角（0度，正上方）
        this.maxPolarAngle = Math.PI; // 最大俯角（180度，正下方）
        
        // 通常限制在接近水平线的范围内，避免翻转
        this.minPolarAngle = Math.PI / 18; // 约 10 度
        this.maxPolarAngle = Math.PI - Math.PI / 18; // 约 170 度
        
        // 距离限制
        this.minDistance = 0;
        this.maxDistance = 100;
        
        // 启用/禁用控制
        this.enabled = true; // 总开关
        this.enableRotate = true;
        this.enablePan = true;
        this.enableZoom = true;
        
        // 鼠标按钮
        this.mouseButtons = {
            LEFT: THREE.MOUSE.ROTATE,
            MIDDLE: THREE.MOUSE.PAN,
            RIGHT: THREE.MOUSE.PAN
        };
        
        // 状态
        this.state = {
            NONE: -1,
            ROTATE: 0,
            PAN: 1,
            DOLLY: 2
        };
        this.currentState = this.state.NONE;
        
        // 鼠标位置
        this.rotateStart = new THREE.Vector2();
        this.rotateEnd = new THREE.Vector2();
        this.rotateDelta = new THREE.Vector2();
        
        this.panStart = new THREE.Vector2();
        this.panEnd = new THREE.Vector2();
        this.panDelta = new THREE.Vector2();
        
        // 绑定事件
        this.onMouseDown = this.onMouseDown.bind(this);
        this.onMouseMove = this.onMouseMove.bind(this);
        this.onMouseUp = this.onMouseUp.bind(this);
        this.onMouseWheel = this.onMouseWheel.bind(this);
        this.onContextMenu = this.onContextMenu.bind(this);
        
        this.domElement.addEventListener('mousedown', this.onMouseDown);
        this.domElement.addEventListener('wheel', this.onMouseWheel);
        this.domElement.addEventListener('contextmenu', this.onContextMenu);
        
        // 初始化球形坐标
        this.update();
    }
    
    // 更新相机位置
    update() {
        const offset = new THREE.Vector3();
        const quat = new THREE.Quaternion().setFromUnitVectors(
            this.camera.up,
            new THREE.Vector3(0, 1, 0)
        );
        const quatInverse = quat.clone().invert();
        
        const position = this.camera.position;
        
        offset.copy(position).sub(this.target);
        offset.applyQuaternion(quat);
        
        this.spherical.setFromVector3(offset);
        
        // 应用旋转
        this.spherical.theta += this.sphericalDelta.theta;
        this.spherical.phi += this.sphericalDelta.phi;
        
        // 限制垂直角度
        this.spherical.phi = Math.max(
            this.minPolarAngle,
            Math.min(this.maxPolarAngle, this.spherical.phi)
        );
        
        this.spherical.makeSafe();
        
        // 应用缩放
        this.spherical.radius *= this.scale;
        
        // 限制距离
        this.spherical.radius = Math.max(
            this.minDistance,
            Math.min(this.maxDistance, this.spherical.radius)
        );
        
        // 应用平移
        this.target.add(this.panOffset);
        
        offset.setFromSpherical(this.spherical);
        offset.applyQuaternion(quatInverse);
        
        position.copy(this.target).add(offset);
        
        this.camera.lookAt(this.target);
        
        // 重置增量
        this.sphericalDelta.set(0, 0, 0);
        this.panOffset.set(0, 0, 0);
        this.scale = 1;
        
        return true;
    }
    
    // 鼠标按下
    onMouseDown(event) {
        if (!this.enabled) return;
        
        event.preventDefault();
        
        switch (event.button) {
            case 0: // 左键 - 旋转
                if (!this.enableRotate) return;
                this.handleRotateStart(event);
                this.currentState = this.state.ROTATE;
                break;
                
            case 1: // 中键 - 平移
                if (!this.enablePan) return;
                this.handlePanStart(event);
                this.currentState = this.state.PAN;
                break;
                
            case 2: // 右键 - 平移
                if (!this.enablePan) return;
                this.handlePanStart(event);
                this.currentState = this.state.PAN;
                break;
        }
        
        if (this.currentState !== this.state.NONE) {
            document.addEventListener('mousemove', this.onMouseMove);
            document.addEventListener('mouseup', this.onMouseUp);
        }
    }
    
    // 鼠标移动
    onMouseMove(event) {
        if (!this.enabled) return;
        
        event.preventDefault();
        
        switch (this.currentState) {
            case this.state.ROTATE:
                if (!this.enableRotate) return;
                this.handleRotateMove(event);
                break;
                
            case this.state.PAN:
                if (!this.enablePan) return;
                this.handlePanMove(event);
                break;
        }
    }
    
    // 鼠标抬起
    onMouseUp(event) {
        if (!this.enabled) return;
        
        document.removeEventListener('mousemove', this.onMouseMove);
        document.removeEventListener('mouseup', this.onMouseUp);
        
        this.currentState = this.state.NONE;
    }
    
    // 鼠标滚轮
    onMouseWheel(event) {
        if (!this.enabled || !this.enableZoom) return;
        
        event.preventDefault();
        event.stopPropagation();
        
        this.handleMouseWheel(event);
    }
    
    // 右键菜单
    onContextMenu(event) {
        if (!this.enabled) return;
        event.preventDefault();
    }
    
    // 处理旋转开始
    handleRotateStart(event) {
        this.rotateStart.set(event.clientX, event.clientY);
    }
    
    // 处理旋转移动
    handleRotateMove(event) {
        this.rotateEnd.set(event.clientX, event.clientY);
        this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart)
            .multiplyScalar(this.rotateSpeed);
        
        const element = this.domElement;
        
        // 水平旋转（绕Y轴）
        this.sphericalDelta.theta -= 2 * Math.PI * this.rotateDelta.x / element.clientHeight;
        
        // 垂直旋转（绕X轴）
        this.sphericalDelta.phi -= 2 * Math.PI * this.rotateDelta.y / element.clientHeight;
        
        this.rotateStart.copy(this.rotateEnd);
        
        this.update();
    }
    
    // 处理平移开始
    handlePanStart(event) {
        this.panStart.set(event.clientX, event.clientY);
    }
    
    // 处理平移移动
    handlePanMove(event) {
        this.panEnd.set(event.clientX, event.clientY);
        this.panDelta.subVectors(this.panEnd, this.panStart)
            .multiplyScalar(this.panSpeed);
        
        this.pan(this.panDelta.x, this.panDelta.y);
        
        this.panStart.copy(this.panEnd);
        
        this.update();
    }
    
    // 平移计算
    pan(deltaX, deltaY) {
        const offset = new THREE.Vector3();
        const element = this.domElement;
        
        const position = this.camera.position;
        offset.copy(position).sub(this.target);
        let targetDistance = offset.length();
        
        // 根据透视投影调整平移速度
        targetDistance *= Math.tan((this.camera.fov / 2) * Math.PI / 180.0);
        
        // 水平平移
        const panLeft = new THREE.Vector3();
        panLeft.setFromMatrixColumn(this.camera.matrix, 0); // X轴
        panLeft.multiplyScalar(-2 * deltaX * targetDistance / element.clientHeight);
        
        // 垂直平移
        const panUp = new THREE.Vector3();
        panUp.setFromMatrixColumn(this.camera.matrix, 1); // Y轴
        panUp.multiplyScalar(2 * deltaY * targetDistance / element.clientHeight);
        
        this.panOffset.add(panLeft).add(panUp);
    }
    
    // 处理缩放
    handleMouseWheel(event) {
        if (event.deltaY < 0) {
            this.dollyIn();
        } else if (event.deltaY > 0) {
            this.dollyOut();
        }
        
        this.update();
    }
    
    dollyIn() {
        this.scale /= Math.pow(0.95, this.zoomSpeed);
    }
    
    dollyOut() {
        this.scale *= Math.pow(0.95, this.zoomSpeed);
    }
    
    // 设置目标点
    setTarget(x, y, z) {
        this.target.set(x, y, z);
        this.update();
    }
    
    /**
     * 聚焦到目标对象
     * @param {THREE.Object3D} object - 要聚焦的对象
     * @param {number} duration - 动画持续时间（毫秒）
     * @param {boolean} selfOnly - 是否只聚焦对象自身的包围盒（不包括子孙节点）
     * @param {number} distanceMultiplier - 距离倍数（默认2.2）
     */
    focusOn(object, duration = 600, selfOnly = false, distanceMultiplier = 2.2) {
        if (!object) {
            console.warn('⚠️ focusOn: 对象为空');
            return;
        }
        
        // 计算对象的包围盒
        let box;
        if (selfOnly) {
            // 只计算对象自身的包围盒（不递归子孙节点，只看直接子节点）
            box = new THREE.Box3();
            
            // 先检查对象自己是否有几何体
            if (object.geometry) {
                object.geometry.computeBoundingBox();
                if (object.geometry.boundingBox) {
                    const tempBox = object.geometry.boundingBox.clone();
                    tempBox.applyMatrix4(object.matrixWorld);
                    box.union(tempBox);
                }
            }
            
            // 遍历直接子节点（不递归）
            if (object.children && object.children.length > 0) {
                object.children.forEach(child => {
                    if (child.geometry) {
                        child.geometry.computeBoundingBox();
                        if (child.geometry.boundingBox) {
                            const childBox = child.geometry.boundingBox.clone();
                            childBox.applyMatrix4(child.matrixWorld);
                            box.union(childBox);
                        }
                    }
                    // 如果子节点是mesh或有可见的对象，也需要考虑
                    if (child.isMesh || child.isLine || child.isPoints) {
                        const childBox = new THREE.Box3().setFromObject(child, false); // false表示不递归
                        if (!childBox.isEmpty()) {
                            box.union(childBox);
                        }
                    }
                });
            }
            
            // 如果包围盒仍然为空，使用对象的世界位置创建一个小包围盒
            if (box.isEmpty()) {
                const worldPosition = new THREE.Vector3();
                object.getWorldPosition(worldPosition);
                const radius = 0.5;
                box.setFromCenterAndSize(
                    worldPosition,
                    new THREE.Vector3(radius, radius, radius)
                );
            }
        } else {
            // 包含所有子孙节点的包围盒（递归）
            box = new THREE.Box3().setFromObject(object);
        }
        
        // 检查包围盒是否有效
        if (box.isEmpty()) {
            console.warn('⚠️ 对象包围盒为空，无法聚焦');
            return;
        }
        
        const size = box.getSize(new THREE.Vector3());
        const center = box.getCenter(new THREE.Vector3());
        
        // 计算包围盒的对角线长度（最能代表对象的整体大小）
        const diagonal = size.length();
        
        // 如果对象太小，设置一个最小尺寸
        const effectiveSize = Math.max(diagonal, 0.1);
        
        const fov = this.camera.fov * (Math.PI / 180);
        
        // 计算相机到目标的距离，让包围盒铺满视野
        // 使用对角线长度和较小的系数，让对象充满视野
        const cameraDistance = effectiveSize / (2 * Math.tan(fov / 2)) * distanceMultiplier;
        
        // 🎯 根据对象包围盒大小动态调整滚轮缩放速度
        // 使用对数缩放，使得速度变化更平滑
        const baseZoomSpeed = 1.0;
        
        // 参考尺寸：5单位（场景的中等尺寸）
        const referenceSize = 5.0;
        
        // 使用对数函数计算缩放比例，让速度变化更自然
        // 对于很小的对象（< 1单位），速度降低到0.2-0.5倍
        // 对于中等对象（5单位左右），速度为1倍
        // 对于很大的对象（> 20单位），速度提升到2-5倍
        const logScale = Math.log10(effectiveSize / referenceSize + 1);
        const speedMultiplier = Math.pow(10, logScale * 0.6); // 0.6是调节系数
        
        // 限制缩放速度范围：最小0.2倍，最大5倍
        this.zoomSpeed = baseZoomSpeed * Math.max(0.2, Math.min(5.0, speedMultiplier));
        // 计算目标相机位置
        // 策略：从对象中心向外，使用当前相机方向
        const currentDirection = new THREE.Vector3();
        currentDirection.subVectors(this.camera.position, this.target).normalize();
        
        // 如果当前方向太接近零向量或太平（没有高度），使用一个合适的默认角度
        if (currentDirection.length() < 0.1 || Math.abs(currentDirection.y) < 0.1) {
            // 使用45度俯视角，从前上方观察
            const horizontalAngle = Math.atan2(currentDirection.x, currentDirection.z) || 0;
            currentDirection.set(
                Math.sin(horizontalAngle),
                0.7,  // 约45度的俯视角
                Math.cos(horizontalAngle)
            ).normalize();
        }
        
        // 计算目标相机位置：对象中心 + 方向 × 距离
        const targetPosition = new THREE.Vector3();
        targetPosition.copy(center).add(currentDirection.multiplyScalar(cameraDistance));
        // 动画参数
        const startPosition = this.camera.position.clone();
        const startTarget = this.target.clone();
        const startTime = Date.now();
        
        // 动画函数
        const animate = () => {
            const elapsed = Date.now() - startTime;
            const progress = Math.min(elapsed / duration, 1);
            
            // 使用缓动函数（ease-in-out）获得更平滑的动画
            const eased = progress < 0.5
                ? 2 * progress * progress
                : 1 - Math.pow(-2 * progress + 2, 2) / 2;
            
            // 插值相机位置
            this.camera.position.lerpVectors(startPosition, targetPosition, eased);
            
            // 插值目标点（相机看向的位置）
            this.target.lerpVectors(startTarget, center, eased);
            
            // 更新相机朝向 - 确保相机始终看向目标中心
            this.camera.lookAt(this.target);
            
            // 继续动画
            if (progress < 1) {
                requestAnimationFrame(animate);
            } else {
                // 动画结束，更新球形坐标系统
                const offset = new THREE.Vector3();
                offset.copy(this.camera.position).sub(this.target);
                
                const quat = new THREE.Quaternion().setFromUnitVectors(
                    this.camera.up,
                    new THREE.Vector3(0, 1, 0)
                );
                offset.applyQuaternion(quat);
                this.spherical.setFromVector3(offset);
            }
        };
        
        // 开始动画
        animate();
    }
    
    // 启用/禁用
    get enabled() {
        return this._enabled !== false;
    }
    
    set enabled(value) {
        this._enabled = value;
    }
    
    // 清理
    dispose() {
        this.domElement.removeEventListener('mousedown', this.onMouseDown);
        this.domElement.removeEventListener('wheel', this.onMouseWheel);
        this.domElement.removeEventListener('contextmenu', this.onContextMenu);
        
        document.removeEventListener('mousemove', this.onMouseMove);
        document.removeEventListener('mouseup', this.onMouseUp);
    }
}

